package com.db.williamchart.extensions;

import android.graphics.Path;
import com.db.williamchart.extensions.Pair;

import com.db.williamchart.data.DataPoint;
import com.db.williamchart.data.Label;
import com.db.williamchart.data.Scale;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ListUtils {

    public static Pair<Float, Float> limits(List<DataPoint> dataPoints) {
        if (dataPoints.isEmpty()) return new Pair<>(0F, 1F);

        List<Float> value = new ArrayList<>();
        for (DataPoint dataPoint : dataPoints) {
            value.add(dataPoint.getValue());
        }

        return floatLimits(value);
    }

    public static Scale toScale(List<DataPoint> dataPoints) {
        Pair<Float, Float> limits = limits(dataPoints);
        return new Scale(limits.first, limits.second);
    }

    public static Scale toBarScale(List<DataPoint> dataPoints) {
        Pair<Float, Float> limits = limits(dataPoints);
        if (limits.first > 0) {
            return new Scale(0f, limits.second);
        } else if (limits.second < 0) {
            new Scale(limits.first, 0f);
        }
        return new Scale(limits.first, limits.second);
    }

    public static List<Label> toLabels(List<DataPoint> dataPoints) {
        List<Label> result = new ArrayList<>();
        for (DataPoint dataPoint : dataPoints) {
            result.add(new Label(dataPoint.getLabel(), 0f, 0f));
        }
        return result;
    }

    public static Path toLinePath(List<DataPoint> dataPoints) {
        Path res = new Path();
        res.moveTo(dataPoints.get(0).getScreenPositionX(), dataPoints.get(0).getScreenPositionY());
        for (int i = 1; i < dataPoints.size(); i++) {
            res.lineTo(dataPoints.get(i).getScreenPositionX(), dataPoints.get(i).getScreenPositionY());
        }
        return res;
    }

    public static Path toSmoothLinePath(List<DataPoint> dataPoints, float smoothFactor) {
        float thisPointX;
        float thisPointY;
        float nextPointX;
        float nextPointY;
        float startDiffX;
        float startDiffY;
        float endDiffX;
        float endDiffY;
        float firstControlX;
        float firstControlY;
        float secondControlX;
        float secondControlY;

        Path res = new Path();
        res.moveTo(dataPoints.get(0).getScreenPositionX(), dataPoints.get(0).getScreenPositionY());

        for (int i = 0; i < dataPoints.size() - 1; i++) {
            thisPointX = dataPoints.get(i).getScreenPositionX();
            thisPointY = dataPoints.get(i).getScreenPositionY();

            nextPointX = dataPoints.get(i + 1).getScreenPositionX();
            nextPointY = dataPoints.get(i + 1).getScreenPositionY();

            startDiffX = nextPointX - dataPoints.get(si(dataPoints.size(), i - 1)).getScreenPositionX();
            startDiffY = nextPointY - dataPoints.get(si(dataPoints.size(), i - 1)).getScreenPositionY();

            endDiffX = dataPoints.get(si(dataPoints.size(), i + 2)).getScreenPositionX() - thisPointX;
            endDiffY = dataPoints.get(si(dataPoints.size(), i + 2)).getScreenPositionY() - thisPointY;

            firstControlX = thisPointX + smoothFactor * startDiffX;
            firstControlY = thisPointY + smoothFactor * startDiffY;

            secondControlX = nextPointX - smoothFactor * endDiffX;
            secondControlY = nextPointY - smoothFactor * endDiffY;

            res.cubicTo(
                    firstControlX,
                    firstControlY,
                    secondControlX,
                    secondControlY,
                    nextPointX,
                    nextPointY
            );
        }

        return res;
    }

    private static Pair<Float, Float> floatLimits(List<Float> value) {
        Collections.sort(value);
        float min = value.get(0);
        float max = value.get(value.size() - 1);

        if (min == max) {
            max += 1F;
        }
        return new Pair<Float, Float>(min, max);
    }

    private static int si(int setSize, int i) {
        if (i > setSize - 1) {
            return setSize - 1;
        } else if (i < 0) {
            return 0;
        }
        return i;
    }

    public static List<DataPoint> toDataPoints(List<Pair<String, Float>> pairs) {
        List<DataPoint> dataPoints = new ArrayList<>();
        for (Pair<String, Float> pair : pairs) {
            dataPoints.add(new DataPoint(pair.first, pair.second, 0f, 0f));
        }
        return dataPoints;
    }
}
